package com.wxapp.system.service.util;

import com.wxapp.common.util.date.DateTimeUtil;
import com.wxapp.common.util.random.RandomUtil;
import com.wxapp.system.service.config.properties.MinioProperties;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Component
public class MinioUtil {
    private static final Logger log = LoggerFactory.getLogger(MinioUtil.class);
    private static final String SEPARATOR_DOT = ".";
    /**
     * 默认过期时间(分钟)
     */
    private final MinioClient minioClient;
    private final MinioProperties minioProperties;

    public MinioUtil(MinioClient minioClient, MinioProperties minioProperties) {
        this.minioClient = minioClient;
        this.minioProperties = minioProperties;
    }

    /**
     * 将分钟数转换为秒数
     *
     * @param expiry 过期时间(分钟数)
     * @return expiry
     */
    private static int expiryHandle(Integer expiry) {
        expiry = expiry * 60;
        if (expiry > 604800) {
            return 604800;
        }
        return expiry;
    }

    /**
     * @return boolean
     * @Description 判断 bucket是否存在
     */
    public boolean bucketExists() {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getBucketName()).build());
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return false;
    }

    /**
     * @param multipartFile
     * @param directory     image/
     * @return java.lang.String
     * @Description 文件上传
     */
    public String upload(MultipartFile multipartFile, String directory) throws Exception {
        if (!bucketExists()) {
            makeBucket(minioProperties.getBucketName());
        }
        InputStream inputStream = multipartFile.getInputStream();
        directory = Optional.ofNullable(directory).orElse("");
        var fileName = minFileName(multipartFile.getOriginalFilename());
        String minFileName = directory + "/" + fileName;
        //上传文件到指定目录
        minioClient.putObject(PutObjectArgs.builder().bucket(minioProperties.getBucketName()).object(minFileName).contentType(multipartFile.getContentType()).stream(inputStream, inputStream.available(), -1).build());
        inputStream.close();
        // 返回生成文件名、访问路径
        return getObjectUrl(minFileName);
    }

    /**
     * @param originalFileName
     * @return java.lang.String
     * @Description 生成上传文件名
     */
    private String minFileName(String originalFileName) {
        String suffix = originalFileName;
        if (originalFileName.contains(SEPARATOR_DOT)) {
            suffix = originalFileName.substring(originalFileName.lastIndexOf(SEPARATOR_DOT));
        }
        return RandomUtil.getCommonRandom(12).toUpperCase()+ DateTimeUtil.getTimeStamp(null) + suffix;
    }

    /**
     * 获取访问对象的外链地址
     * 获取文件的下载url
     *
     * @param objectName 对象名称
     * @return viewUrl
     */
    public String getObjectUrl(String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(minioProperties.getBucketName()).object(objectName).build());
    }

    /**
     * 创建存储桶
     * 创建 bucket
     *
     * @param bucketName
     */
    public void makeBucket(String bucketName) {
        try {
            boolean isExist = bucketExists();
            if (!isExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 删除一个文件
     *
     * @param objectName
     */
    public boolean removeObject(String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        objectName = objectName.replace("_", "/");
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioProperties.getBucketName()).object(objectName).build());
        return true;
    }

    /**
     * @param objectNames
     * @return java.util.List<java.lang.String>
     * @Description 删除指定桶的多个文件对象, 返回删除错误的对象列表，全部删除成功，返回空列表
     */
    public List<String> removeObjects(List<String> objectNames) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        if (!bucketExists()) {
            return new ArrayList<>();
        }
        List<DeleteObject> deleteObjects = new ArrayList<>(objectNames.size());
        for (String objectName : objectNames) {
            deleteObjects.add(new DeleteObject(objectName));
        }
        List<String> deleteErrorNames = new ArrayList<>();
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(minioProperties.getBucketName()).objects(deleteObjects).build());
        for (Result<DeleteError> result : results) {
            DeleteError error = result.get();
            deleteErrorNames.add(error.objectName());
        }
        return deleteErrorNames;
    }

    /**
     * 获取文件
     */
    public InputStream getFile(String name) {
        try {
            name = name.replace("_", "/");
            return minioClient.getObject(GetObjectArgs.builder().bucket(minioProperties.getBucketName()).object(name).build());
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException("获取文件失败");
        }
    }
}